package mhcs.client;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.TreeSet;

/**
 * Datastructure that holds modules in traversable alphabetical order
 * based on serial number. Improper adds and edits may throw exceptions that
 * specify the improper input.
 * @author Galen
 * @version 0.2
 * Added an edit method so input can be checked internally.
 */
public class ModuleSet {

    /**
     * Constructor builds the TreeSet for modules and ArrayList of listeners.
     */
    public ModuleSet() {
        listeners = new ArrayList<MyListener>();
        t = new TreeSet<Module>();
        
        configuration = 0;
        soundFlag = false;
    }

    /**
     * Add a new module.
     * @param srl   The serial number of the module to be added.
     * @param stts  The damage status of the module.
     * @param rttn  The number of rotations the module needs.
     * @param ex    The module's X-coordinate.
     * @param why   The module's Y-coordinate.
     * @return bool returns false if the serial number or x-y values are taken.
     * @throws Exception :
     * Serial number out of range throws an exception.
     * X-Y coordinates out of range or in unusable area throws an exception.
     */
    public final boolean add(final int srl, final String stts, final int rttn,
                        final int ex, final int why) throws Exception {
        Iterator<Module> iter = t.iterator();
        Boolean bool = true;
        int x = ex;
        int y = why;
        if (x < 1 || x > 100 || y < 1 || y > 50) {
            bool = false;
        }
        if (x >= 40 && x <= 50 && y >= 40 && y <= 50) {
            bool = false;
        }
        if (!bool) {
            throw new Exception("These X-Y coordinates are outside the landing"
                    + " area or inside the unuseable area.");
        }
        String type = process(srl);
        if (type == null) {
            throw new Exception("Serial numbers must be within the range"
                            + " of 001-190");
        }
        while (iter.hasNext()) {
            Module next = iter.next();
            if (next.getX() == ex
                    && next.getY() == why) {
                bool = false;
            }
        }
        if (bool) {
            Module module = new Module(srl, type, stts, rttn, ex, why);
            bool = t.add(module);
            if (bool) {
                ListIterator<MyListener> it = listeners.listIterator();
                while (it.hasNext()) {
                    it.next().actionPerformed("add",  module);
                }
                
                if(soundFlag == false){
                	MHCS.soundOutput.playSound0();  	
                }
                
            }
        }
        return bool;
    }

    /**
     * Removes a module from the dataSet based on the input serial number.
     * @param serial : the serial number of the module to be removed.
     * @return false if that serial number is not in the data set.
     */
    public final boolean remove(final int serial) {
        Iterator<Module> iter = t.iterator();
        Boolean found = false;
        Module module = new Module(serial, "", "", 0, 0, 0);
        Module mod = module;
        while (iter.hasNext() && !found) {
            mod = iter.next();
            if (mod.compareTo(module) == 0) {
                found = t.remove(mod);
                ListIterator<MyListener> it = listeners.listIterator();
                while (it.hasNext()) {
                    it.next().actionPerformed("remove", mod);
                }
                
                if(soundFlag == false){
                	MHCS.soundOutput.playSound1();  	
                }
                
            }
        }
        
        return found;
    }

    /**
     * Edits a module in the data set.
     * @param serial        module's serial number.
     * @param status        module's new damage status.
     * @param orientation   new number of rotations the module needs.
     * @param x             module's new x coordinate
     * @param y             module's new y coordinate
     * @return              false if the destination is taken, the serial
     * number is not in the log, or the serial number is out of range.
     * @throws Exception edit uses add, so it throws the same exceptions for the
     * x-y coordinates being out of range or in the unusable area.
     */
    public boolean edit(final int serial, final String status,
            final int orientation, final int x, final int y) throws Exception{
        boolean destinationOpen = true;
        boolean hasModule = false;
        boolean success = false;
        String type = process(serial);
        Iterator<Module> it = t.iterator();
        Module module;
        while (it.hasNext()) {
            module = it.next();
            if (module.getX() == x && module.getY() == y) {
                destinationOpen = false;
            }
            if (module.getSerial() == serial) {
                hasModule = true;
            }
        }
        if (destinationOpen && hasModule && type != null) {

        	MHCS.soundOutput.playSound2();
        	soundFlag = true;
        	
        	remove(serial);	
            success = add(serial, status, orientation, x, y);
            
            soundFlag = false;
        }
        return success;
    }

    /**
     * Add a new listener (extending MyListener class)
     * to be activated on add/remove/edit.
     * @param listener  the listener to be added.
     * @return a boolean success value.
     */
    public final boolean addListener(final MyListener listener) {
        return listeners.add(listener);
    }
    
    public boolean clear() {
    	ListIterator<MyListener> iter = listeners.listIterator();
        Module module = new Module(0, "", "", 0,0, 0);
        while (iter.hasNext()) {
            iter.next().actionPerformed("clear", module);
        }
        t = new TreeSet<Module>();
        return t.isEmpty();
    }
    
    public void setConfiguration(int config) {
    	configuration = config;
    	ListIterator<MyListener> iter = listeners.listIterator();
    	while (iter.hasNext()){
    		iter.next().actionPerformed( "set", new Module(config, "", "", 0 ,0 ,0));
    	}
    }
    
    public int getConfiguration(){
    	return configuration;
    }
    

    /**
     * No idea why I added a removeListener method, but here it is!
     * @param listener the listener to be added.
     * @return a boolean success value.
     */
    public final boolean removeListener(final MyListener listener) {
        return listeners.remove(listener);
    }

    private String process(int serial) {
        String ret = null;
        if (serial > 0 && serial <= 190) {
            ret = "";
        }
        if (serial > 0 && serial <= 40) {
            ret = "Plain";
        }
        if (serial > 60 && serial <= 80) {
            ret = "Dormitory";
        }
        if (serial > 90 && serial <= 100) {
            ret = "Sanitation";
        }
        if (serial > 110 && serial <= 120) {
            ret = "Food";
        }
        if (serial > 130 && serial <= 134) {
            ret = "Gym";
        }
        if (serial > 140 && serial <= 144) {
            ret = "Canteen";
        }
        if (serial > 150 && serial <= 154) {
            ret = "Power";
        }
        if (serial > 160 && serial <= 164) {
            ret = "Control";
        }
        if (serial > 170 && serial <= 174) {
            ret = "Airlock";
        }
        if (serial > 180 && serial <= 184) {
            ret = "Medical";
        }
        return ret;
    }

    public TreeSet<Module> getTree(){
    	return t;
    }
    private ArrayList<MyListener> listeners;

    private TreeSet<Module> t;
    private int configuration;

    //Used to see if prevent both edit module and remove module sound clips playing at the same
    //time when you edit a module.
    public static boolean soundFlag;


}
